// Code file original author: Martin Lapierre, http://devinstinct.com

using System;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Validation.Properties;

namespace Microsoft.Practices.EnterpriseLibrary.Validation.Validators
{

	/// <summary>
	/// Validates that a value is of, or inherits, a specific type.
	/// </summary>
	/// <remarks>
	/// A null value is not considered to be of the same type (convention of the 'is' operator).
	/// By following that convention we make sure that null won't be valid for value-types such as DateTime.
	/// This validator is mainly used for downcasting.
	/// </remarks>
	/// <typeparam name="T">The target type.</typeparam>
	public class TypeValidator<T> :
		  ValueValidator<T>
	{
		#region Constructors

		/// <summary>
		/// Default constructor.
		/// </summary>
		public TypeValidator()
			: base(null, null, false)
		{
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="messageTemplate">
		/// The template to use when logging validation results, 
		/// or null we the default message template is to be used.
		/// </param>
		/// <param name="tag">
		/// The tag to set when logging validation results, or null.
		/// </param>
		/// <param name="negated">
		/// Indicates if the validation logic represented by the validator should be negated.
		/// </param>
		public TypeValidator(string messageTemplate, string tag, bool negated)
			: base(messageTemplate, tag, negated)
		{
		}

		#endregion Constructors


		#region Properties

		/// <summary>
		/// Gets the Default Message Template when the validator is negated.
		/// </summary>
		protected override string DefaultNegatedMessageTemplate
		{
			get
			{
				return Resources.TypeValidatorNegatedDefaultMessageTemplate;
			}
		}

		/// <summary>
		/// Gets the Default Message Template when the validator is non negated.
		/// </summary>
		protected override string DefaultNonNegatedMessageTemplate
		{
			get
			{
				return Resources.TypeValidatorNonNegatedDefaultMessageTemplate;
			}
		}

		#endregion Properties


		#region Methods

		/// <summary>
		/// Implements the validation logic for the receiver.
		/// </summary>
		/// <param name="objectToValidate">The instance of T to validate.</param>
		/// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
		/// <param name="key">The key that identifies the source of objectToValidate.</param>
		/// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
		protected override void DoValidate(T objectToValidate, object currentTarget, string key, ValidationResults validationResults)
		{
			// Do not call DoValidate(object...) overload to prevent boxing of value types and loss of type information for the 'is' operator.
			if ((objectToValidate is T) == Negated)
				LogValidationResult(validationResults, GetMessage(objectToValidate, key), currentTarget, key);
		}

		/// <summary>
		/// Implements the validation logic for the receiver.
		/// </summary>
		/// <param name="objectToValidate">The object to validate.</param>
		/// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
		/// <param name="key">The key that identifies the source of objectToValidate.</param>
		/// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
		protected internal override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
		{
			if ((objectToValidate is T) == Negated)
				LogValidationResult(validationResults, GetMessage(objectToValidate, key), currentTarget, key);
		}

		/// <summary>
		/// Gets the message representing a failed validation.
		/// </summary>
		/// <param name="objectToValidate">The object for which validation was performed.</param>
		/// <param name="key">The key representing the value being validated for <paramref name="objectToValidate"/>.</param>
		/// <returns>The message representing the validation failure.</returns>
		protected override string GetMessage(object objectToValidate, string key)
		{
			return string.Format(CultureInfo.CurrentUICulture,
				 this.MessageTemplate,
				 objectToValidate,
				 key,
				 this.Tag,
				 objectToValidate == null ? "(null)" : objectToValidate.GetType().ToString(),
				 typeof(T).ToString());
		}

		#endregion Methods
	}
}
